/* verilator lint_off UNUSEDSIGNAL */
module execute #(
    parameter STQ_SIZE = 8,
    parameter ALU_NUM = 2,
    parameter BRU_NUM = 1,
    parameter LSU_NUM = 1,
    parameter MDU_NUM = 1,
    parameter MISC_NUM = 1,
    parameter RF_WRITE_WIDTH = ALU_NUM + BRU_NUM + LSU_NUM + MDU_NUM + MISC_NUM
) (
    input            clk,
    input            rst,
    //From Issue Stage
    input            i_alu_valid               [       ALU_NUM-1:0],
    input  ISSUE_OP  i_alu_iop                 [       ALU_NUM-1:0],
    output           i_alu_ready,
    input            i_bru_valid               [       BRU_NUM-1:0],
    input  ISSUE_OP  i_bru_iop                 [       BRU_NUM-1:0],
    output           i_bru_ready,
    input            i_lsu_valid               [       LSU_NUM-1:0],
    input  ISSUE_OP  i_lsu_iop                 [       LSU_NUM-1:0],
    output           i_lsu_ready,
    input            i_mdu_valid               [       MDU_NUM-1:0],
    input  ISSUE_OP  i_mdu_iop                 [       MDU_NUM-1:0],
    output           i_mdu_ready,
    input            i_misc_valid              [      MISC_NUM-1:0],
    input  ISSUE_OP  i_misc_iop                [      MISC_NUM-1:0],
    output           i_misc_ready,
    output           o_mdu_early_wakeup_valid  [       MDU_NUM-1:0],
    output PRF_IDX   o_mdu_early_wakeup_prf_idx[       MDU_NUM-1:0],
    //Memory Req Port
    output           o_mem_req_valid,
    output MEM_REQ   o_mem_req,
    input            o_mem_req_ready,
    output           o_mem_req_kill_prev,
    //Memory Rsp Port
    input            i_mem_rsp_valid,
    input  MEM_RSP   i_mem_rsp,
    output           i_mem_rsp_ready,
    //Read/Write Port
    output CSR_ADDR  csr_addr,
    output           csr_read,
    output           csr_write,
    output           csr_set,
    output           csr_clear,
    output word_t    csr_wdata,
    input  word_t    csr_rdata,
    input  word_t    csr_mepc,
    input  word_t    csr_mtvec,
    //Regfile Write Port
    output           o_write_valid             [RF_WRITE_WIDTH-1:0],
    output PRF_IDX   o_write_prf_idx           [RF_WRITE_WIDTH-1:0],
    output word_t    o_write_prf_data          [RF_WRITE_WIDTH-1:0],
    //ROB modify
    output           o_rob_finish_valid        [RF_WRITE_WIDTH-1:0],
    output ROB_PTR   o_rob_finish_rob_idx      [RF_WRITE_WIDTH-1:0],
    output           o_rob_exeception_valid    [RF_WRITE_WIDTH-1:0],
    output ROB_PTR   o_rob_exeception_rob_idx  [RF_WRITE_WIDTH-1:0],
    output EXPT_CODE o_rob_exeception_code     [RF_WRITE_WIDTH-1:0],
    output           o_rob_redirect_valid,
    output ROB_PTR   o_rob_redirect_rob_idx,
    output word_t    o_rob_redirect_pc,
    //ROB order
    input            i_rob_order_valid,
    input  ROB_PTR   i_rob_order_rob_idx,
    //ROB MMIO
    output           o_rob_mmio_valid          [       LSU_NUM-1:0],
    output ROB_PTR   o_rob_mmio_rob_idx        [       LSU_NUM-1:0],
    //ROB branch miss
    output           o_rob_bru_bpred_miss_valid[       BRU_NUM-1:0],
    output ROB_PTR   o_rob_bru_rob_idx         [       BRU_NUM-1:0],
    output word_t    o_rob_bru_dnpc            [       BRU_NUM-1:0],
    output BPU_Res   o_rob_bru_correct_bpu_res [       BRU_NUM-1:0]
);
  logic i_alu_ready_unpack[ALU_NUM-1:0];
  logic i_bru_ready_unpack[BRU_NUM-1:0];
  logic i_lsu_ready_unpack[LSU_NUM-1:0];
  logic i_mdu_ready_unpack[MDU_NUM-1:0];
  logic i_misc_ready_unpack[MISC_NUM-1:0];
  logic [ALU_NUM-1:0] i_alu_ready_pack;
  logic [BRU_NUM-1:0] i_bru_ready_pack;
  logic [LSU_NUM-1:0] i_lsu_ready_pack;
  logic [MDU_NUM-1:0] i_mdu_ready_pack;
  logic [MISC_NUM-1:0] i_misc_ready_pack;
  assign i_alu_ready  = &i_alu_ready_pack;
  assign i_bru_ready  = &i_bru_ready_pack;
  assign i_lsu_ready  = &i_lsu_ready_pack;
  assign i_mdu_ready  = &i_mdu_ready_pack;
  assign i_misc_ready = &i_misc_ready_pack;

  pack #(1, ALU_NUM) pack_i_alu_ready (
      .din (i_alu_ready_unpack),
      .dout(i_alu_ready_pack)
  );
  pack #(1, BRU_NUM) pack_i_bru_ready (
      .din (i_bru_ready_unpack),
      .dout(i_bru_ready_pack)
  );
  pack #(1, LSU_NUM) pack_i_lsu_ready (
      .din (i_lsu_ready_unpack),
      .dout(i_lsu_ready_pack)
  );
  pack #(1, MDU_NUM) pack_i_mdu_ready (
      .din (i_mdu_ready_unpack),
      .dout(i_mdu_ready_pack)
  );
  pack #(1, MISC_NUM) pack_i_misc_ready (
      .din (i_misc_ready_unpack),
      .dout(i_misc_ready_pack)
  );
  genvar gi;
  generate
    //ALU
    logic     o_alu_valid                 [ALU_NUM-1:0];
    logic     o_alu_ready                 [ALU_NUM-1:0];
    ISSUE_OP  o_alu_iop                   [ALU_NUM-1:0];
    word_t    o_alu_res                   [ALU_NUM-1:0];

    logic     o_alu_write_valid           [ALU_NUM-1:0];
    PRF_IDX   o_alu_write_prf_idx         [ALU_NUM-1:0];
    word_t    o_alu_write_prf_data        [ALU_NUM-1:0];

    logic     o_alu_rob_finish_valid      [ALU_NUM-1:0];
    ROB_PTR   o_alu_rob_finish_rob_idx    [ALU_NUM-1:0];
    logic     o_alu_rob_exeception_valid  [ALU_NUM-1:0];
    ROB_PTR   o_alu_rob_exeception_rob_idx[ALU_NUM-1:0];
    EXPT_CODE o_alu_rob_exeception_code   [ALU_NUM-1:0];
    for (gi = 0; gi < ALU_NUM; gi = gi + 1) begin : ALUs
      assign o_alu_ready[gi] = 'b1;
      alu inst_alu (
          .i_valid(i_alu_valid[gi]),
          .i_iop  (i_alu_iop[gi]),
          .i_ready(i_alu_ready_unpack[gi]),
          .o_valid(o_alu_valid[gi]),
          .o_ready(o_alu_ready[gi]),
          .o_iop  (o_alu_iop[gi]),
          .o_res  (o_alu_res[gi]),
          .*
      );
      assign o_alu_write_valid[gi] = o_alu_valid[gi] & o_alu_ready[gi] & o_alu_iop[gi].rop.fop.dop.has_rd;
      assign o_alu_write_prf_idx[gi] = o_alu_iop[gi].rop.pdst;
      assign o_alu_write_prf_data[gi] = o_alu_res[gi];

      assign o_alu_rob_finish_valid[gi] = o_alu_valid[gi] & o_alu_ready[gi];
      assign o_alu_rob_finish_rob_idx[gi] = o_alu_iop[gi].rob_idx;
      assign o_alu_rob_exeception_valid[gi] = 'b0;
      assign o_alu_rob_exeception_rob_idx[gi] = 'b0;
      assign o_alu_rob_exeception_code[gi] = 'b0;
    end
    //BRU
    logic     o_bru_valid                 [BRU_NUM-1:0];
    ISSUE_OP  o_bru_iop                   [BRU_NUM-1:0];
    word_t    o_bru_res                   [BRU_NUM-1:0];
    logic     o_bru_taken                 [BRU_NUM-1:0];
    word_t    o_bru_dnpc                  [BRU_NUM-1:0];
    BPU_Res   o_bru_correct_bpu_res       [BRU_NUM-1:0];
    logic     o_bru_bpred_miss            [BRU_NUM-1:0];
    logic     o_bru_ready                 [BRU_NUM-1:0];

    logic     o_bru_write_valid           [BRU_NUM-1:0];
    PRF_IDX   o_bru_write_prf_idx         [BRU_NUM-1:0];
    word_t    o_bru_write_prf_data        [BRU_NUM-1:0];

    logic     o_bru_rob_finish_valid      [BRU_NUM-1:0];
    ROB_PTR   o_bru_rob_finish_rob_idx    [BRU_NUM-1:0];
    logic     o_bru_rob_exeception_valid  [BRU_NUM-1:0];
    ROB_PTR   o_bru_rob_exeception_rob_idx[BRU_NUM-1:0];
    EXPT_CODE o_bru_rob_exeception_code   [BRU_NUM-1:0];
    for (gi = 0; gi < BRU_NUM; gi = gi + 1) begin : BRUs
      assign o_bru_ready[gi] = 'b1;
      bru inst_bru (
          .i_valid(i_bru_valid[gi]),
          .i_iop(i_bru_iop[gi]),
          .i_ready(i_bru_ready_unpack[gi]),
          .o_valid(o_bru_valid[gi]),
          .o_ready(o_bru_ready[gi]),
          .o_iop(o_bru_iop[gi]),
          .o_taken(o_bru_taken[gi]),
          .o_dnpc(o_bru_dnpc[gi]),
          .o_correct_bpu_res(o_bru_correct_bpu_res[gi]),
          .o_bpred_miss(o_bru_bpred_miss[gi]),
          .o_res(o_bru_res[gi]),
          .*
      );
      assign o_bru_write_valid[gi] = o_bru_valid[gi] & o_bru_ready[gi] & o_bru_iop[gi].rop.fop.dop.has_rd;
      assign o_bru_write_prf_idx[gi] = o_bru_iop[gi].rop.pdst;
      assign o_bru_write_prf_data[gi] = o_bru_res[gi];

      assign o_bru_rob_finish_valid[gi] = o_bru_valid[gi] & o_bru_ready[gi];
      assign o_bru_rob_finish_rob_idx[gi] = o_bru_iop[gi].rob_idx;
      assign o_bru_rob_exeception_valid[gi] = 'b0;
      assign o_bru_rob_exeception_rob_idx[gi] = 'b0;
      assign o_bru_rob_exeception_code[gi] = 'b0;

      assign o_rob_bru_bpred_miss_valid[gi] = o_bru_valid[gi]&o_bru_ready[gi]&o_bru_bpred_miss[gi];
      assign o_rob_bru_rob_idx[gi] = o_bru_iop[gi].rob_idx;
      assign o_rob_bru_dnpc[gi] = o_bru_dnpc[gi];
      assign o_rob_bru_correct_bpu_res[gi] = o_bru_correct_bpu_res[gi];
    end
    //AGU
    logic    o_agu_valid[LSU_NUM-1:0];
    logic    o_agu_ready[LSU_NUM-1:0];
    ISSUE_OP o_agu_iop  [LSU_NUM-1:0];
    MEM_REQ  o_agu_req  [LSU_NUM-1:0];
    agu inst_agu (
        .i_valid(i_lsu_valid[0]),
        .i_iop  (i_lsu_iop[0]),
        .i_ready(i_lsu_ready_unpack[0]),
        .o_valid(o_agu_valid[0]),
        .o_ready(o_agu_ready[0]),
        .o_iop  (o_agu_iop[0]),
        .o_req  (o_agu_req[0]),
        .*
    );

    assign o_rob_mmio_valid[0]   = o_agu_valid[0] & o_lsu_ready[0] & o_agu_req[0].mmio;
    assign o_rob_mmio_rob_idx[0] = o_agu_iop[0].rob_idx;

    logic     lsu_empty;

    logic     o_lsu_write_valid           [LSU_NUM-1:0];
    PRF_IDX   o_lsu_write_prf_idx         [LSU_NUM-1:0];
    word_t    o_lsu_write_prf_data        [LSU_NUM-1:0];

    logic     o_lsu_valid                 [LSU_NUM-1:0];
    MEM_RSP   o_lsu_rsp                   [LSU_NUM-1:0];
    logic     o_lsu_ready                 [LSU_NUM-1:0];
    logic     o_lsu_rob_finish_valid      [LSU_NUM-1:0];
    ROB_PTR   o_lsu_rob_finish_rob_idx    [LSU_NUM-1:0];
    logic     o_lsu_rob_exeception_valid  [LSU_NUM-1:0];
    ROB_PTR   o_lsu_rob_exeception_rob_idx[LSU_NUM-1:0];
    EXPT_CODE o_lsu_rob_exeception_code   [LSU_NUM-1:0];
    assign o_lsu_ready[0] = 'b1;
    lsu #(STQ_SIZE) inst_lsu (
        .empty(lsu_empty),
        .i_valid(o_agu_valid[0]),
        .i_req(o_agu_req[0]),
        .i_ready(o_agu_ready[0]),
        .o_valid(o_lsu_valid[0]),
        .o_rsp(o_lsu_rsp[0]),
        .o_ready(o_lsu_ready[0]),
        .o_rob_finish_valid(o_lsu_rob_finish_valid[0]),
        .o_rob_finish_rob_idx(o_lsu_rob_finish_rob_idx[0]),
        .o_dmem_valid(o_mem_req_valid),
        .o_dmem_req(o_mem_req),
        .o_dmem_ready(o_mem_req_ready),
        .o_dmem_kill_prev(o_mem_req_kill_prev),
        .i_dmem_valid(i_mem_rsp_valid),
        .i_dmem_rsp(i_mem_rsp),
        .i_dmem_ready(i_mem_rsp_ready),
        .*
    );

    assign o_lsu_write_valid[0] = o_lsu_valid[0] & o_lsu_ready[0] & o_lsu_rsp[0].has_data & o_lsu_rsp[0].has_rd;
    assign o_lsu_write_prf_idx[0] = o_lsu_rsp[0].prf_idx;
    assign o_lsu_write_prf_data[0] = o_lsu_rsp[0].data;
    assign o_lsu_rob_exeception_valid[0] = 'b0;
    assign o_lsu_rob_exeception_code[0] = 'b0;
    assign o_lsu_rob_exeception_rob_idx[0] = 'b0;

    //MDU
    logic     o_mdu_valid                 [MDU_NUM-1:0];
    logic     o_mdu_ready                 [MDU_NUM-1:0];
    ISSUE_OP  o_mdu_iop                   [MDU_NUM-1:0];
    word_t    o_mdu_res                   [MDU_NUM-1:0];

    logic     o_mdu_write_valid           [MDU_NUM-1:0];
    PRF_IDX   o_mdu_write_prf_idx         [MDU_NUM-1:0];
    word_t    o_mdu_write_prf_data        [MDU_NUM-1:0];

    logic     o_mdu_rob_finish_valid      [MDU_NUM-1:0];
    ROB_PTR   o_mdu_rob_finish_rob_idx    [MDU_NUM-1:0];
    logic     o_mdu_rob_exeception_valid  [MDU_NUM-1:0];
    ROB_PTR   o_mdu_rob_exeception_rob_idx[MDU_NUM-1:0];
    EXPT_CODE o_mdu_rob_exeception_code   [MDU_NUM-1:0];

    for (gi = 0; gi < MDU_NUM; gi = gi + 1) begin : MDUs
      assign o_mdu_ready[gi] = 'b1;
      mdu inst_mdu (
          .i_valid(i_mdu_valid[gi]),
          .i_iop(i_mdu_iop[gi]),
          .i_ready(i_mdu_ready_unpack[gi]),
          .o_valid(o_mdu_valid[gi]),
          .o_ready(o_mdu_ready[gi]),
          .o_iop(o_mdu_iop[gi]),
          .o_res(o_mdu_res[gi]),
          .o_early_wakeup_valid(o_mdu_early_wakeup_valid[gi]),
          .o_early_wakeup_prf_idx(o_mdu_early_wakeup_prf_idx[gi]),
          .*
      );
      assign o_mdu_write_valid[gi] = o_mdu_valid[gi] & o_mdu_ready[gi]& o_mdu_iop[gi].rop.fop.dop.has_rd;
      assign o_mdu_write_prf_idx[gi] = o_mdu_iop[gi].rop.pdst;
      assign o_mdu_write_prf_data[gi] = o_mdu_res[gi];
      assign o_mdu_rob_finish_valid[gi] = o_mdu_valid[gi] & o_mdu_ready[gi];
      assign o_mdu_rob_finish_rob_idx[gi] = o_mdu_iop[gi].rob_idx;
      assign o_mdu_rob_exeception_valid[gi] = 'b0;
      assign o_mdu_rob_exeception_rob_idx[gi] = 'b0;
      assign o_mdu_rob_exeception_code[gi] = 'b0;
    end

    //MISC
    logic     o_misc_valid                 [MISC_NUM-1:0];
    logic     o_misc_ready                 [MISC_NUM-1:0];
    ISSUE_OP  o_misc_iop                   [MISC_NUM-1:0];
    word_t    o_misc_res                   [MISC_NUM-1:0];

    logic     o_misc_write_valid           [MISC_NUM-1:0];
    PRF_IDX   o_misc_write_prf_idx         [MISC_NUM-1:0];
    word_t    o_misc_write_prf_data        [MISC_NUM-1:0];

    logic     o_misc_rob_finish_valid      [MISC_NUM-1:0];
    ROB_PTR   o_misc_rob_finish_rob_idx    [MISC_NUM-1:0];
    logic     o_misc_rob_exeception_valid  [MISC_NUM-1:0];
    ROB_PTR   o_misc_rob_exeception_rob_idx[MISC_NUM-1:0];
    EXPT_CODE o_misc_rob_exeception_code   [MISC_NUM-1:0];

    assign o_misc_ready[0] = 'b1;
    misc inst_misc (
        .i_valid(i_misc_valid[0]),
        .i_iop(i_misc_iop[0]),
        .i_ready(i_misc_ready_unpack[0]),
        .o_valid(o_misc_valid[0]),
        .o_ready(o_misc_ready[0]),
        .o_iop(o_misc_iop[0]),
        .o_res(o_misc_res[0]),
        .o_rob_exeception_valid(o_misc_rob_exeception_valid[0]),
        .o_rob_exeception_rob_idx(o_misc_rob_exeception_rob_idx[0]),
        .o_rob_exeception_code(o_misc_rob_exeception_code[0]),
        .*
    );
    assign o_misc_write_valid[0] = o_misc_valid[0] & o_misc_ready[0] & o_misc_iop[0].rop.fop.dop.has_rd;
    assign o_misc_write_prf_idx[0] = o_misc_iop[0].rop.pdst;
    assign o_misc_write_prf_data[0] = o_misc_res[0];
    assign o_misc_rob_finish_valid[0] = o_misc_valid[0] & o_misc_ready[0];
    assign o_misc_rob_finish_rob_idx[0] = o_misc_iop[0].rob_idx;
  endgenerate

  assign o_write_valid = {
    o_alu_write_valid, o_bru_write_valid, o_lsu_write_valid, o_mdu_write_valid, o_misc_write_valid
  };
  assign o_write_prf_idx = {
    o_alu_write_prf_idx,
    o_bru_write_prf_idx,
    o_lsu_write_prf_idx,
    o_mdu_write_prf_idx,
    o_misc_write_prf_idx
  };
  assign o_write_prf_data = {
    o_alu_write_prf_data,
    o_bru_write_prf_data,
    o_lsu_write_prf_data,
    o_mdu_write_prf_data,
    o_misc_write_prf_data
  };

  assign o_rob_finish_valid = {
    o_alu_rob_finish_valid,
    o_bru_rob_finish_valid,
    o_lsu_rob_finish_valid,
    o_mdu_rob_finish_valid,
    o_misc_rob_finish_valid
  };
  assign o_rob_finish_rob_idx = {
    o_alu_rob_finish_rob_idx,
    o_bru_rob_finish_rob_idx,
    o_lsu_rob_finish_rob_idx,
    o_mdu_rob_finish_rob_idx,
    o_misc_rob_finish_rob_idx
  };
  assign o_rob_exeception_valid = {
    o_alu_rob_exeception_valid,
    o_bru_rob_exeception_valid,
    o_lsu_rob_exeception_valid,
    o_mdu_rob_exeception_valid,
    o_misc_rob_exeception_valid
  };
  assign o_rob_exeception_rob_idx = {
    o_alu_rob_exeception_rob_idx,
    o_bru_rob_exeception_rob_idx,
    o_lsu_rob_exeception_rob_idx,
    o_mdu_rob_exeception_rob_idx,
    o_misc_rob_exeception_rob_idx
  };
  assign o_rob_exeception_code = {
    o_alu_rob_exeception_code,
    o_bru_rob_exeception_code,
    o_lsu_rob_exeception_code,
    o_mdu_rob_exeception_code,
    o_misc_rob_exeception_code
  };
endmodule
/* verilator lint_on UNUSEDSIGNAL */

